const jsonpath = require("jsonpath");
const Api = require("./Api");
const Field = require("./Field");


class FieldRelationshipAnalyzer {

    /**
     * 构造函数，用于初始化 FieldRelationshipAnalyzer 类的实例。
     * 该构造函数接收 API 列表、强制请求字段配置列表、强制响应字段配置列表、请求字段映射配置列表和响应字段映射配置列表，
     * 并将这些参数存储为实例属性。同时，初始化一个空的变量数组，用于存储分析过程中找到的可复用字段。
     * 
     * @param {Api[]} apis - 一个包含 Api 实例的数组，代表要分析的 API 列表。
     * @param {{urlPath: string, name: string}[]} forcedRequestConfigs - 强制请求字段配置列表，每个元素是一个包含 `urlPath` 和 `name` 属性的对象。
     * @param {{urlPath: string, name: string}[]} forcedResponseConfigs - 强制响应字段配置列表，每个元素是一个包含 `urlPath` 和 `name` 属性的对象。
     * @param {{field1: {urlPath: string, name: string}, field2: {urlPath: string, name: string}}[]} mapRequestConfigs - 请求字段映射配置列表，每个元素是一个包含 `field1` 和 `field2` 属性的对象。
     * @param {{field1: {urlPath: string, name: string}, field2: {urlPath: string, name: string}}[]} mapResponseConfigs - 响应字段映射配置列表，每个元素是一个包含 `field1` 和 `field2` 属性的对象。
     * @param {{urlPath: string, name: string}[]} ignoreRequestConfigs - 忽略请求字段配置列表，每个元素是一个包含 `urlPath` 和 `name` 属性的对象。
     * @param {{urlPath: string, name: string}[]} ignoreResponseConfigs - 忽略响应字段配置列表，每个元素是一个包含 `urlPath` 和 `name` 属性的对象。
     */
    constructor(apis, forcedRequestConfigs, forcedResponseConfigs, mapRequestConfigs, mapResponseConfigs, ignoreRequestConfigs, ignoreResponseConfigs) {
        /** @type {Api[]} */
        this.apis = apis;
        /** @type {{urlPath: string, name: string}[]} */
        this.forcedRequestConfigs = forcedRequestConfigs;
        /** @type {{urlPath: string, name: string}[]} */
        this.forcedResponseConfigs = forcedResponseConfigs;
        /** @type {{field1: {urlPath: string, name: string}, field2: {urlPath: string, name: string}}[]} */
        this.mapRequestConfigs = mapRequestConfigs;
        /** @type {{field1: {urlPath: string, name: string}, field2: {urlPath: string, name: string}}[]} */
        this.mapResponseConfigs = mapResponseConfigs;
        /** @type {{urlPath: string, name: string}[]} */
        this.ignoreRequestConfigs = ignoreRequestConfigs;
        /** @type {{urlPath: string, name: string}[]} */
        this.ignoreResponseConfigs = ignoreResponseConfigs;
        /** @type {{apiId: string, type: string, urlPath: string, name: string, value: string, jsonPath: string, relationships: string[]}[]} */
        this.variables = [];
    }

    /**
     * 检查当前 API 字段是否可复用。
     * 该方法会先验证字段名称是否不是纯数字且字段有值，
     * 然后遍历当前 API 之后的所有 API，检查是否有请求字段的值和名称与当前字段匹配。
     * 如果找到匹配的字段，则认为当前字段可复用。
     * 
     * @param {Field} currentApiField - 当前要检查的 API 字段对象。
     * @param {number} currentApiIndex - 当前 API 在 API 列表中的索引。
     * @returns {boolean} 如果字段可复用则返回 true，否则返回 false。
     */
    isFieldReusable(currentApiField, currentApiIndex) {
        if (currentApiField.value === null) {
            return false;
        }
        if (typeof currentApiField.value === "string" && currentApiField.value.trim() === "") {
            return false;
        }
        if (!(isNaN(Number(currentApiField.name)) && currentApiField.value)) {
            return false;
        }
        for (let i = currentApiIndex + 1; i < this.apis.length; i++) {
            const nextApi = this.apis[i];
            for (const nextApiRequestField of nextApi.requestFields) {
                if (nextApiRequestField.value === currentApiField.value && nextApiRequestField.name === currentApiField.name) {
                    return true;
                }
            }
        }
        return false;
    }


    /**
     * 根据传入的字段对象查找匹配的变量。
     * 该方法会按照以下顺序查找变量：
     * 1. 查找类型包含 "FORCED" 且值与传入字段值相等的强制变量。
     * 2. 若未找到强制变量，则查找类型为 "REQUEST_MAP" 或 "RESPONSE_MAP" 且满足映射关系的映射变量。
     * 3. 若仍未找到匹配变量，则查找名称去除末尾数字后与字段名称相等且值相等的变量。
     * 如果以上都未找到匹配的变量，则返回 null。
     * 
     * @param {Field} field - 用于查找匹配变量的字段对象，应包含 `name`、`value` 和 `urlPath` 属性。
     * @returns {{apiId: string, type: string, urlPath: string, name: string, value: string, jsonPath: string, relationships: string[]}|null} - 匹配的变量对象，如果未找到则返回 null。
     */
    findVariableByField(field) {
        for (let i = 0; i < this.variables.length; i++) {
            const variable = this.variables[i];
            // 强制字段
            if (variable.value === field.value && variable.type.includes("FORCED")) {
                return variable;
            }
        }
        for (let i = 0; i < this.variables.length; i++) {
            const variable = this.variables[i];
            // 通用字段
            if (variable.value === field.value && variable.name.replace(/\d+$/, "") === field.name) {
                return variable;
            }
            // 映射字段
            if (variable.value === field.value && variable.type === "REQUEST_MAP") {
                if (this.mapRequestConfigs.find(mapRequestConfig =>
                    mapRequestConfig.field1.urlPath === variable.urlPath
                    && mapRequestConfig.field1.name === variable.name.replace(/\d+$/, "")
                    && (mapRequestConfig.field2.urlPath === "*" ? true : mapRequestConfig.field2.urlPath === field.urlPath)
                    && mapRequestConfig.field2.name === field.name)) {
                    return variable;
                }
            }
            if (variable.value === field.value && variable.type === "RESPONSE_MAP") {
                if (this.mapResponseConfigs.find(mapResponseConfig =>
                    mapResponseConfig.field1.urlPath === variable.urlPath
                    && mapResponseConfig.field1.name === variable.name.replace(/\d+$/, "")
                    && (mapResponseConfig.field2.urlPath === "*" ? true : mapResponseConfig.field2.urlPath === field.urlPath)
                    && mapResponseConfig.field2.name === field.name)) {
                    return variable;
                }
            }

        }
        return null;
    }

    /**
     * 生成一个唯一的变量名。
     * 该方法会根据传入的字段名称生成变量名，若生成的变量名已存在于变量列表中，
     * 则在字段名称后面添加递增的数字，直到生成一个唯一的变量名。
     * 
     * @param {Field} field - 用于生成变量名的字段对象，应包含 `name` 属性。
     * @returns {string} 生成的唯一变量名。
     */
    generateVariableName(field) {
        let counter = 1;
        let variableName = field.name;
        while (this.variables.find(variable => variable.name === variableName)) {
            variableName = `${field.name}${counter++}`;
        }
        return variableName;
    }

    /**
     * 分析 API 列表，找出所有可复用的字段并将其作为变量存储。
     * 该方法会遍历所有 API，分别处理强制请求字段、强制响应字段、映射请求字段、映射响应字段、
     * 通用请求字段、通用响应字段和大值字段。最后用变量替换可复用的字段，并对变量的关联关系进行去重处理。
     * 
     * @returns {{apiId: string, type: string, urlPath: string, name: string, value: string, jsonPath: string, relationships: string[]}[]} - 包含所有可复用变量的数组。
     */
    analyze() {

        // 遍历所有的 API
        this.apis.forEach((api, currentIndex) => {

            // 遍历当前 API 的所有强制请求字段
            this.forcedRequestConfigs.forEach(forcedRequestConfig => {
                if (forcedRequestConfig.urlPath === api.url.pathname) {
                    api.requestFields.forEach(field => {
                        if (field.name === forcedRequestConfig.name && (field.value !== null && field.value !== "")) {
                            if (!this.variables.find(variable => variable.type === "REQUEST_FORCED"
                                && variable.name.replace(/\d+$/, "") === field.name
                                && variable.value === field.value)) {
                                this.variables.push({
                                    apiId: api.id,
                                    type: "REQUEST_FORCED",
                                    urlPath: api.url.pathname,
                                    name: this.generateVariableName(field),
                                    value: field.value,
                                    jsonPath: field.jsonPath,
                                    relationships: [],
                                });
                            }
                        }
                    });
                }
            });

            // 遍历当前 API 的所有强制响应字段
            this.forcedResponseConfigs.forEach(forcedResponseConfig => {
                if (forcedResponseConfig.urlPath === api.url.pathname) {
                    api.responseFields.forEach(field => {
                        if (field.name === forcedResponseConfig.name && field.value !== null) {
                            if (!this.variables.find(variable => variable.type === "RESPONSE_FORCED"
                                && variable.name.replace(/\d+$/, "") === field.name
                                && variable.value === field.value)) {
                                this.variables.push({
                                    apiId: api.id,
                                    type: "RESPONSE_FORCED",
                                    urlPath: api.url.pathname,
                                    name: this.generateVariableName(field),
                                    value: field.value,
                                    jsonPath: field.jsonPath,
                                    relationships: [],
                                });
                            }
                        }
                    });
                }
            });

            // 遍历当前 API 的所有映射请求字段
            this.mapRequestConfigs.forEach(mapRequestConfig => {
                if (mapRequestConfig.field1.urlPath === api.url.pathname) {
                    api.requestFields.forEach(field => {
                        if (!this.variables.find(variable => variable.type === "REQUEST_MAP"
                            && variable.name.replace(/\d+$/, "") === field.name
                            && variable.value === field.value)) {
                            this.variables.push({
                                apiId: api.id,
                                type: "REQUEST_MAP",
                                urlPath: api.url.pathname,
                                name: this.generateVariableName(field),
                                value: field.value,
                                jsonPath: field.jsonPath,
                                relationships: []
                            });
                        }
                    });
                }
            });

            // 遍历当前 API 的所有映射响应字段
            this.mapResponseConfigs.forEach(mapResponseConfig => {
                if (mapResponseConfig.field1.urlPath === api.url.pathname) {
                    api.responseFields.forEach(field => {
                        if (field.name === mapResponseConfig.field1.name) {
                            if (!this.variables.find(variable => variable.type === "RESPONSE_MAP"
                                && variable.name.replace(/\d+$/, "") === field.name
                                && variable.value === field.value)) {
                                this.variables.push({
                                    apiId: api.id,
                                    type: "RESPONSE_MAP",
                                    urlPath: api.url.pathname,
                                    name: this.generateVariableName(field),
                                    value: field.value,
                                    jsonPath: field.jsonPath,
                                    relationships: []
                                });
                            }
                        }
                    });
                }
            });


            // 遍历当前 API 的所有请求字段
            api.requestFields.forEach(field => {
                // 忽略请求字段
                if (this.ignoreRequestConfigs.find(ignoreRequestConfig => (ignoreRequestConfig.urlPath === api.url.pathname
                    || ignoreRequestConfig.urlPath === "*")
                    && (ignoreRequestConfig.name === field.name || ignoreRequestConfig.name === "*"))) {
                    return;
                }
                // 通用请求字段
                if (this.isFieldReusable(field, currentIndex)) {
                    if (!this.findVariableByField(field)) {
                        if (typeof field.value === "string") {
                            // 处理日期
                            field.value = field.value.replace(/"(\d{4}-\d{2}-\d{2})(( \d{2}:\d{2}:\d{2})?)"/g, (match, datePart, timePart) => {
                                if (datePart === new Date().toISOString().split("T")[0]) {
                                    return `"\${__time(yyyy-MM-dd,)}${timePart || ""}"`;
                                }
                                return match;
                            }
                            );
                            // 处理换行符
                            field.value = field.value.replace(/\n/g, "\\n").replace(/\r/g, "\\r");
                            // 处理双引号
                            field.value = field.value.replace(/"/g, '\\"');
                        }
                        this.variables.push({
                            apiId: api.id,
                            type: "REQUEST_GENERAL",
                            urlPath: api.url.pathname,
                            name: this.generateVariableName(field),
                            value: field.value,
                            jsonPath: field.jsonPath,
                            relationships: [],
                        });
                    }
                }
            });

            // 遍历当前 API 的所有响应字段
            api.responseFields.forEach(field => {
                // 忽略响应字段
                if (this.ignoreResponseConfigs.find(ignoreResponseConfig => (ignoreResponseConfig.urlPath === api.url.pathname
                    || ignoreResponseConfig.urlPath === "*")
                    && (ignoreResponseConfig.name === field.name || ignoreResponseConfig.name === "*"))) {
                    return;
                }
                // 通用响应字段
                if (this.isFieldReusable(field, currentIndex)) {
                    if (!this.findVariableByField(field)) {
                        this.variables.push({
                            apiId: api.id,
                            type: "RESPONSE_GENERAL",
                            urlPath: api.url.pathname,
                            name: this.generateVariableName(field),
                            value: field.value,
                            jsonPath: field.jsonPath,
                            relationships: [],
                        });
                    }
                }
            });

            // 遍历当前 API 的所有大值字段
            api.requestFields.forEach(field => {
                if (!this.findVariableByField(field) && typeof field.value === "string" && field.value.length > 10) {
                    // 处理换行符
                    field.value = field.value.replace(/\n/g, "\\n").replace(/\r/g, "\\r");
                    // 处理双引号
                    field.value = field.value.replace(/"/g, '\\"');
                    this.variables.push({
                        apiId: api.id,
                        type: "LARGE_VALUE",
                        urlPath: api.url.pathname,
                        name: this.generateVariableName(field),
                        value: field.value,
                        relationships: [],
                    });
                }
            });

            // 遍历当前 API 的所有请求字段，用变量替换可复用的字段
            api.requestFields.forEach(field => {
                const variable = this.findVariableByField(field);
                if (variable) {
                    // number需要特殊处理
                    jsonpath.apply(api.request, field.jsonPath, () => `$\{${variable.name}${typeof variable.value === "number" ? "@number" : ""}\}`);
                    let relationship = api.id
                    if (variable.name.replace(/\d+$/, "") !== field.name) {
                        relationship = `${api.id}(${field.name})`;
                    }
                    variable.relationships.push(relationship);
                }
            });

            // 对每个变量的关联关系进行去重处理
            this.variables.forEach(variable => {
                variable.relationships = [...new Set(variable.relationships)];
            });
        });

        // 返回包含所有可复用变量的数组
        return this.variables;
    }

}

module.exports = FieldRelationshipAnalyzer;